// Copyright (C) Jari Hautio <jari.hautio@iki.fi> 2008. Licensed under GPLv2. See LICENSE.txt in solution folder.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Resources;

namespace RyijyApp
{
    public partial class Form1 : Form
    {
		const int MaxReduceColors = 2 * 4096;
        ImageProcessor imageProcessor;
        Recolorer recolorer = new Recolorer();
        Bitmap loadedImage = null;
        Bitmap croppedImage = null;
        enum ColorOrdering { SortByColor, SortByCount, SortByBrightness, SortBySaturation };
        ColorOrdering colorOrder = ColorOrdering.SortByColor;

        enum DrawingToolType { None, Pen, Pick };
        Dictionary<DrawingToolType, DrawingTool> tools = new Dictionary<DrawingToolType, DrawingTool>();
       DrawingToolType currentTool = DrawingToolType.None;
       List<Color> addedColors = new List<Color>();
       List<CustomPalette> customPalettes = new List<CustomPalette>();

	   List<int> selectedColors = new List<int>();

		Pen dotpen = new Pen(Color.Black,1);

        public Form1()
        {
            imageProcessor = new ImageProcessor();
            InitializeComponent();
            ryijyPropBindingSource.DataSource = imageProcessor.RyijyProperties;
            comboBoxColorPresets.SelectedIndex = 0;
            imageProcessor.ColorsChanged += new EventHandler(imageProcessor_ColorsChanged);
            comboBoxSortColors.SelectedIndex = 0;
            imageProcessor.ResetHandler += new EventHandler(imageProcessor_ResetHandler);
            CreateTools();
            recolorer.CanUndoChanged += new EventHandler(recolorer_CanUndoChanged);
            recolorer.ImageChanged += new EventHandler(recolorer_ImageChanged);
            finnishToolStripMenuItem.Checked = System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName == "fi";
            englishToolStripMenuItem.Checked = System.Threading.Thread.CurrentThread.CurrentUICulture.TwoLetterISOLanguageName == "en";

			dotpen.DashStyle = DashStyle.Dot;

		}

        void recolorer_ImageChanged(object sender, EventArgs e)
        {
            drawingCanvas1.UpdateImage(false);
            UpdateColors();
        }

        void recolorer_CanUndoChanged(object sender, EventArgs e)
        {
            buttonColorBack.Enabled = recolorer.CanUndo;
        }
        void CreateTools()
        {
            // Picker
            DrawingTool d = new DrawingTool();
            tools.Add(DrawingToolType.Pick, d);
            d.Cursor = Cursors.Arrow;
            d.OnClicked += new MouseEventHandler(Picker_OnClicked);
            // Pen
            d = new DrawingTool();
            tools.Add(DrawingToolType.Pen, d);
            d.Cursor = Cursors.Cross;
            d.OnMoved += new MouseEventHandler(Pen_OnClicked);
            d.OnDown += new MouseEventHandler(Pen_OnClicked);            
        }

        void Pen_OnClicked(object sender, MouseEventArgs e)
        {
            if (tabControl1.SelectedIndex != 2) return;
            if (selectedColors.Count == 0)
                return;
            Color c = Color.FromArgb(selectedColors[0]);
            Point p = drawingCanvas1.ConvertToImage(e.Location);
            recolorer.SetPixel(p.X, p.Y, c);
        }
        void Picker_OnClicked(object sender, MouseEventArgs e)
        {
            if (tabControl1.SelectedIndex != 2) return;
            Color c = drawingCanvas1.GetPixel(e.Location);
			if (c != Color.Empty)
            {
				int n = 0;
				foreach (ListViewItem li in itemCache.Keys)
                {
                    if (li.BackColor == c)
                    {
						listView1.EnsureVisible(n);
						li.Checked = !li.Checked;
						listView1.RedrawItems(li.Index, li.Index, true);
						bool inselected = selectedColors.BinarySearch(c.ToArgb()) >=0;
						if (li.Checked && !inselected)
							selectedColors.Add(c.ToArgb());
						else if (!li.Checked && inselected)
							selectedColors.Remove(c.ToArgb());

						break;
                    }
					n++;
				}
            }
        }

        void imageProcessor_ResetHandler(object sender, EventArgs e)
        {

            addedColors.Clear();
        }

        void imageProcessor_ColorsChanged(object sender, EventArgs e)
        {
            if (this.InvokeRequired)
            {
                Invoke(new EventHandler( imageProcessor_ColorsChanged), new object[] {e});
                return;
            }
            drawingCanvas1.Picture = imageProcessor.TargetImage;
            drawingCanvas1.UpdateImage(true);
            recolorer.Image = imageProcessor.TargetImage;
            recolorer.Histogram = imageProcessor.TargetHistogram;
            UpdateColors();
        }

        private void avaaToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (openFileDialog1.ShowDialog() == DialogResult.OK)
            {
                CustomPalette loadedPalette = null;
                try
                {
                    if (openFileDialog1.FileName.EndsWith(".rsd.png"))
                    {
                        RyijyPng rpng = new RyijyPng();
                        rpng.Load(openFileDialog1.FileName);
                        loadedImage = new Bitmap(rpng.Image);
                        loadedPalette = rpng.Palette;
                    }
                    else loadedImage = new Bitmap(openFileDialog1.FileName);
                }
                catch (Exception)
                {
                    MessageBox.Show(RyijyApp.Properties.Resources.ErrorOpenFailed);
                    return;
                }
                croppedImage = loadedImage;
                this.Text = openFileDialog1.FileName + " - " + RyijyApp.Properties.Resources.MainTitle;
                imageProcessor.OriginalImage = null;
                drawingCanvas1.Picture = loadedImage;

                comboBoxColorPresets.SelectedIndex = 0;
                comboBoxSizes.SelectedIndex = 0;
                textBoxHeight.Text = loadedImage.Height.ToString();
                textBoxWidth.Text = loadedImage.Width.ToString();
                textBoxCropDown.Text = "0";
                textBoxCropLeft.Text = "0";
                textBoxCropRight.Text = "0";
                textBoxCropUp.Text = "0";
                buttonNext.Enabled = true;
                label13.Visible = false;
                drawingCanvas1.FitToWindow = true;
                imageProcessor.OriginalImage = loadedImage;
                UpdateInfoBox();
                saveFileDialog1.FileName = "";
                if (loadedPalette != null)
                {
                    int n = comboBoxPalette.FindStringExact(loadedPalette.PaletteName);
                    if (n<0)
                    {
                        loadedPalette.UserPalette = true;
                        customPalettes.Add(loadedPalette);
                        comboBoxPalette.Items.Add(loadedPalette.PaletteName);
                        n = comboBoxPalette.FindStringExact(loadedPalette.PaletteName);
                    }
                    comboBoxPalette.SelectedIndex = n;
                    UpdateColorList(true);
                }
            }
        }
        private void UpdateInfoBox()
        {
            labelKuva.Text = String.Format(Properties.Resources.ImagePropertiesText,
                    loadedImage.Height, loadedImage.Width, imageProcessor.OriginalColors);
        }
        private void comboBoxSizes_SelectedIndexChanged(object sender, EventArgs e)
        {
            int index = comboBoxSizes.SelectedIndex;
            if ( index >= 0)
            {
                textBoxHeight.Text = imageProcessor.RyijyProperties.Sizes[index].Height.ToString();
                textBoxWidth.Text = imageProcessor.RyijyProperties.Sizes[index].Width.ToString();
                if (checkBoxLock.Checked) SetAspectRatio();
                buttonResize_Click(this, EventArgs.Empty);
            }
            else // do nothing if nothing is selected
            {
            }
        }

        private void SetAspectRatio()
        {
            Bitmap img = imageProcessor.OriginalImage;
            if ( img == null || img.Width == 0 || img.Height== 0) return;

            int h = Convert.ToInt32(textBoxHeight.Text);
            int w = Convert.ToInt32(textBoxWidth.Text);
            if (h > 0)
            {
                float ratio = Convert.ToSingle(img.Width) / img.Height;
                textBoxWidth.Text = Convert.ToInt32(ratio * h).ToString();
            }
            else
            {
                float ratio = Convert.ToSingle(img.Height) / img.Width;
                textBoxHeight.Text = Convert.ToInt32(ratio * w).ToString();
            }
        }

        private void checkBoxLock_CheckedChanged(object sender, EventArgs e)
        {
            if (checkBoxLock.Checked)
            {
                SetAspectRatio();
            }
        }

        private void textBoxHeight_Leave(object sender, EventArgs e)
        {
            if (checkBoxLock.Checked)
            {
                SetAspectRatio();
            }
        }

        private void textBoxWidth_TextChanged(object sender, EventArgs e)
        {
           
        }

        private void textBoxWidth_Leave(object sender, EventArgs e)
        {
            if (checkBoxLock.Checked)
            {
                // trick to cause SetAspectRatio to update height
                textBoxHeight.Text = "0";
                SetAspectRatio();
            }

        }

        private void textBoxHeight_Validating(object sender, CancelEventArgs e)
        {
            TextBox tb = (TextBox) sender;
            try
            {
                Convert.ToInt32(tb.Text);
                errorProvider1.Clear();
                comboBoxSizes.SelectedIndex = -1;
            }
            catch (Exception)
            {
                e.Cancel = true;
                errorProvider1.SetError(tb, String.Format(Properties.Resources.ErrorSizeNotInteger, tb.Tag.ToString()));
            }
        }

        private void buttonResize_Click(object sender, EventArgs e)
        {
            if (imageProcessor.OriginalImage == null) return;
            int h = Convert.ToInt32(textBoxHeight.Text);
            int w = Convert.ToInt32(textBoxWidth.Text);
            imageProcessor.ResizeImage(w, h);
            // TODO: This should got hrough event!
            comboBoxPalette.SelectedIndex = 0;
            comboBoxColorPresets.SelectedIndex = 0;
            trackBarColors2.Value = trackBarColors2.Maximum;
        }

        private void UpdateCropCues()
        {
            Bitmap bmPhoto = drawingCanvas1.Picture;
            Rectangle r = new Rectangle();
            try
            {
                int t = Convert.ToInt32(textBoxCropUp.Text);
                if (t > 0 && t <= 100)
                {
                    r.Y = t * bmPhoto.Height / 100;
                }
                else r.Y = 0;

                t = Convert.ToInt32(textBoxCropDown.Text);
                if (t > 0 && t <= 100)
                {
                    t = (100 - t) * bmPhoto.Height / 100;
                    r.Height = t - r.Top;
                }
                else r.Height = bmPhoto.Height - r.Top;

                t = Convert.ToInt32(textBoxCropLeft.Text);
                if (t > 0 && t <= 100)
                {
                    r.X = t * bmPhoto.Width / 100;
                }
                else r.X = 0;

                t = Convert.ToInt32(textBoxCropRight.Text);
                if (t > 0 && t <= 100)
                {
                    t = (100 - t) * bmPhoto.Width / 100;
                    r.Width = t - r.Left;
                }
                else r.Width = bmPhoto.Width - r.Left;
                drawingCanvas1.CropCue.CropRect = r;
                drawingCanvas1.UpdateImage(false);
            }
            catch (Exception) { }
        }
        private void pictureBox1_SizeChanged(object sender, EventArgs e)
        {
//            UpdatePictureBox();
        }

        private void buttonLoad_Click(object sender, EventArgs e)
        {
            avaaToolStripMenuItem_Click(sender, e);
        }


        void UpdateColors()
        {
			if (loadedImage == null) return;

            labelOrignalColors.Text = imageProcessor.ScaledColors.ToString();
            labelColorsNow.Text =imageProcessor.TargetColors.ToString();

            // color list
            UpdateColorList(false);

			if (imageProcessor.TargetColors > MaxReduceColors)
            {
                trackBarColors2.Enabled = false;
                textBoxColorTarget.Enabled = false;
                labelColorTarget.Visible = false;
                labelColorsError.Visible = true;
            }
            else
            {
                trackBarColors2.Enabled = true;
                textBoxColorTarget.Enabled = true;
                labelColorTarget.Visible = true;
                labelColorsError.Visible = false;
            }

            trackBarColors2.Minimum = imageProcessor.MinRemovedColors;
            trackBarColors2.Maximum = imageProcessor.MaxRemovedColors;
            label7.Text = trackBarColors2.Maximum.ToString();
            if (trackBarColors2.Value != imageProcessor.RemovedColors)
            {
                trackBarColors2.Value = imageProcessor.RemovedColors;
            }
            labelColorTarget.Text = trackBarColors2.Value.ToString();
            textBoxColorTarget.Text = trackBarColors2.Value.ToString();
            SetColorTargetLabel();
        }
        void SetColorTargetLabel()
        {
            string s = labelColorTarget.Text;
            
            s = s.Substring(0,s.LastIndexOf(':')+1);
            labelColorTarget.Text = s + " " + trackBarColors2.Value;
        }
        class MyComparer : IComparer<ListViewItem>
        {
            public MyComparer(ColorOrdering orderingMode)
            {
                mode = orderingMode;
            }
            ColorOrdering mode;
            #region IComparer<ListViewItem> Members

            public int Compare(ListViewItem x, ListViewItem y)
            {
                float hx, hy;
                switch (mode)
                {
                    case ColorOrdering.SortByColor:
                        hx = x.BackColor.GetHue();
                        hy = y.BackColor.GetHue();
                        if (Math.Abs(hx - hy) > 0.001) 
                        {
                            if (hx < hy) return -1;
                            else if (hx > hy) return 1;
                        }
                        break;
                    case ColorOrdering.SortByBrightness:
                        hx = x.BackColor.GetBrightness();
                        hy = y.BackColor.GetBrightness();
                        if (Math.Abs(hx - hy) > 0.001)
                        {
                            if (hx < hy) return -1;
                            else if (hx > hy) return 1;
                        }
                        break;
                    case ColorOrdering.SortBySaturation:
                        hx = x.BackColor.GetSaturation();
                        hy = y.BackColor.GetSaturation();
                        if (Math.Abs(hx - hy) > 0.001)
                        {
                            if (hx < hy) return -1;
                            else if (hx > hy) return 1;
                        }
                        break;

                case ColorOrdering.SortByCount:
                    try
                    {
                        int nx = Convert.ToInt32(x.Text);
                        int ny = Convert.ToInt32(y.Text);
                        if (nx < ny) return -1;
                        else if (nx > ny) return 1;
                        break;

                    }
                    catch (Exception )
                    {
                    }
                    break;
                }
                return x.BackColor.ToArgb() < y.BackColor.ToArgb() ? -1 :
                    x.BackColor.ToArgb() > y.BackColor.ToArgb() ? 1 : 0;
            }


            #endregion
        }
        bool UpdateColorListCounts()
        {
			if (itemCache == null) return false;

            int added = 0;
            foreach (Color c in addedColors)
            {
                if (!imageProcessor.TargetHistogram.ContainsKey(c))
                    added++;
            }
            // Quick exit if counts don't match
			if (imageProcessor.TargetHistogram.Count + added != itemCache.Count)
                return false;
            int n = 0;
            int a = 0;
            foreach (ListViewItem l in itemCache.Keys)
            {
                int count =0;
                if (!imageProcessor.TargetHistogram.TryGetValue(l.BackColor, ref count))
                {
                    if (addedColors.Contains(l.BackColor))
                    {
                        count = 0;
                        a++;
                    }
                    else return false;
                } else n++;
                string text = count.ToString();
                if (l.SubItems[0].Text != text)
                    l.SubItems[0].Text = text;
            }
			if (n == imageProcessor.TargetHistogram.Count && a == added)
			{
				listView1.Invalidate();
				return true;
			}
			else return false;
        }
		SortedList<ListViewItem, int> itemCache = null;
		private void listView1_CacheVirtualItems(object sender, CacheVirtualItemsEventArgs e)
		{
			if (itemCache == null)
				UpdateColorList(true);			
		}

		private void listView1_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e)
		{
			e.Item = itemCache.Keys[e.ItemIndex];
		}
		/// <summary>
		/// UpdateColorList helper for automatic cases where heavy processing is skipped
		/// </summary>
		/// <param name="force">if false, then first try to update only color counts</param>
		void UpdateColorList(bool force)
		{
			UpdateColorList(force, false);
		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="force"></param>
		/// <param name="manualUpdate">If manual update is not defined then try to skip heavy prosessing.</param>
		void UpdateColorList(bool force, bool manualUpdate)
        {
            // Check first if its enough to update counts
            if (!force && UpdateColorListCounts()) return;

			if (imageProcessor.TargetHistogram.Count > 10000 && !manualUpdate)
			{
				itemCache = new SortedList<ListViewItem, int>(0,new MyComparer(colorOrder));;
				listView1.VirtualListSize = 0;
				listView1.Invalidate();
				listView1.Enabled = false;
				buttonUpdateColorlist.Visible = true;
				labelUpdateColorList.Visible = true;
				return;
			}
			listView1.Enabled = true;
			buttonUpdateColorlist.Visible = false;
			labelUpdateColorList.Visible = false;

			listView1.View = View.Details;
            // Get colors
            Font f = this.Font;
            IComparer<ListViewItem> comp = new MyComparer(colorOrder);
            itemCache = new SortedList<ListViewItem, int>(imageProcessor.TargetHistogram.Count,
                new MyComparer(colorOrder));

			foreach (KeyValuePair<Color, int> x in imageProcessor.TargetHistogram)
            {
                string text ="";
                if (imageProcessor.CustomColorPalette != null)
                {
                    text = imageProcessor.CustomColorPalette.GetTextAndName(x.Key);
                }

				ListViewItem li = new ListViewItem(new string[] { x.Value.ToString(), text } );
                li.BackColor = x.Key;
                if (li.BackColor.GetBrightness() < 0.4)
                    li.ForeColor = Color.White;
                li.ToolTipText = text;
				li.Checked = selectedColors.Contains(li.BackColor.ToArgb());
				itemCache.Add(li, 0);
				
            }
            foreach (Color c in addedColors)
            {
                string text = "";
                if (imageProcessor.CustomColorPalette != null)
                {
                    text = imageProcessor.CustomColorPalette.GetText(c);
                }
                ListViewItem li = new ListViewItem(new string[] { "0", text });
                li.BackColor = c;
                if (li.BackColor.GetBrightness() < 0.4)
                    li.ForeColor = Color.White;
                li.Checked = selectedColors.Contains(li.BackColor.ToArgb());
                if (itemCache.ContainsKey(li)) continue;
				itemCache.Add(li, 0);
            }

			listView1.VirtualListSize = itemCache.Keys.Count;
			listView1.Invalidate();
//			listView1.Columns[1].Width = listView1.ClientSize.Width - listView1.Columns[0].Width;
        }
        private void trackBarColors2_Scroll(object sender, EventArgs e)
        {
            if (sender == trackBarColors2)
            {
                if (textBoxColorTarget.Text != trackBarColors2.Value.ToString())
                    textBoxColorTarget.Text = trackBarColors2.Value.ToString();
            }
            if (trackBarColors2.Value != imageProcessor.RemovedColors)
                SetColorTargetLabel();

			if (imageProcessor.TargetDepthColors <= MaxReduceColors)
            {
                int val = trackBarColors2.Value;
                if (val != imageProcessor.RemovedColors)
                {
                    Cursor = Cursors.WaitCursor;
                    imageProcessor.RemovedColors = val;
                    // TODO: This should got hrough event!
                    Cursor = Cursors.Default;
                }
            }

        }

        private void comboBoxColorPresets_SelectedIndexChanged(object sender, EventArgs e)
        {
            switch (comboBoxColorPresets.SelectedIndex)
            {
                case 0: // Original
                    comboBoxPalette.SelectedIndex = 0;
                    trackBarColors2.Value = trackBarColors2.Maximum;
                    break;
                case 1: // plenty
                    comboBoxPalette.SelectedIndex = 2;
                    trackBarColors2.Value = Math.Min(64, trackBarColors2.Maximum);
                    break;
                case 2: // balanced
                    comboBoxPalette.SelectedIndex = 3;
                    trackBarColors2.Value = Math.Min(32, trackBarColors2.Maximum);
                    break;
                case 3: // low
                    comboBoxPalette.SelectedIndex = 3;
                    trackBarColors2.Value = Math.Min(16, trackBarColors2.Maximum);
                    break;
                case 4: // DMC
                    comboBoxPalette.SelectedIndex = 4;
                    trackBarColors2.Value = Math.Min(32, trackBarColors2.Maximum);
                    break;
            }
            // execute color reduction
            imageProcessor.RemovedColors = trackBarColors2.Value;
            
        }
        private CustomPalette LoadDMC()
        {
            CustomPalette cp = new CustomPalette();
            try
            {
                string fn = Application.StartupPath + "\\dmc.txt";
                if (!cp.Load(fn))
                {
                    MessageBox.Show("Failed to load DMC palette");
                }
                else
                {
                    cp.PaletteName = "DMC";
                }
            }
            catch (Exception)
            { }
            return cp;
        }

        private void comboBoxPalette_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (comboBoxPalette.SelectedIndex < 0) return;
            int numColors = 0;
            switch (comboBoxPalette.SelectedIndex)
            {
                case 0: numColors = 256; break;
                case 1: numColors = 32; break;
                case 2: numColors = 16; break;
                case 3: numColors = 8; break;
                case 4: numColors = -1; break;
                default: numColors = -2; break;
            }
            if (numColors < 0 )
            {
                Cursor = Cursors.WaitCursor;
                CustomPalette cp = new CustomPalette();
                try
                {
                    string fn ="";
                    if (numColors == -1)
                    {
                        fn = Application.StartupPath + "\\" + "dmc.txt" ;
                        if (!cp.Load(fn))
                        {
                            MessageBox.Show("Failed to load DMC palette");
                            return;
                        }
                        cp.PaletteName = comboBoxPalette.Text;
                    }
                    else
                    {
                        foreach(CustomPalette p in customPalettes)
                        {
                            if (p.PaletteName == comboBoxPalette.SelectedItem.ToString())
                            {
                                cp = p;
                                break;
                            }
                        }
                    }


                    imageProcessor.CustomColorPalette = cp;
                    if (imageProcessor.TargetDepth != 0)
                    {
                        imageProcessor.TargetDepth = 0;
                    }
                    else
                    {
                        imageProcessor.UpdateTargetImage();
                    }

                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
                Cursor = Cursors.Default;
            }
            else
            {
                imageProcessor.CustomColorPalette = null;
                if (imageProcessor.TargetDepth != numColors)
                {
                    Cursor = Cursors.WaitCursor;
                    imageProcessor.TargetDepth = numColors;
                    Cursor = Cursors.Default;
                }
            }
        }

        private void buttonRemove_Click(object sender, EventArgs e)
        {
            if (imageProcessor.TargetDepthColors > 3000)
            {
                MessageBox.Show(RyijyApp.Properties.Resources.ErrorTooLargePalette);
                comboBoxPalette.Focus();
                return;
            }
            int val = trackBarColors2.Value;
            if (val != imageProcessor.RemovedColors)
            {
                Cursor = Cursors.WaitCursor;
                imageProcessor.RemovedColors = val;
                // TODO: This should got hrough event!
                Cursor = Cursors.Default;
            }
        }

        private void buttonClose_Click(object sender, EventArgs e)
        {
            Close();

        }

        private void buttonUserReplace_Click(object sender, EventArgs e)
        {
            if (selectedColors.Count == 0) return;

            Color newColor;
            // collect selected colors
            List<Color> colors = new List<Color>();
            int[] cc = new int[selectedColors.Count];
            int i = 0;
			foreach (int argb in selectedColors)
            {
				Color c = Color.FromArgb(argb);
                colors.Add(c);

                int tmp = Color.FromArgb(0, c.B, c.G, c.R).ToArgb();
                cc[i] = tmp;//c.ToArgb();
                i++;
            }

            // show custom color dialog or normal windows color dialog
            if (imageProcessor.CustomColorPalette != null)
            {
                ColorForm cf = new ColorForm();
                cf.ColorPalette = imageProcessor.CustomColorPalette;
                if (cf.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
                newColor = cf.SelectedColor;
            }
            else
            {
                colorDialog1.CustomColors = cc;
                if (colorDialog1.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
                newColor = Color.FromArgb(colorDialog1.Color.R, colorDialog1.Color.G, colorDialog1.Color.B);
            }

            recolorer.RemapColors(colors, newColor);
        }

        private void buttonUserReset_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;

            addedColors.Clear();
            recolorer.Revert();
            Cursor = Cursors.Default;
        }

        private void buttonNext_Click(object sender, EventArgs e)
        {
            if (tabControl1.SelectedIndex < (tabControl1.TabCount - 1))                
                tabControl1.SelectedIndex++;
            if (tabControl1.SelectedIndex == tabControl1.TabCount - 1)
                buttonNext.Enabled = false;
        }

        private void tabPage2_Click(object sender, EventArgs e)
        {

        }

        class MyColorItem
        {
            public MyColorItem(int n, Color c) { num = n; color= c; } 
            public int num;
            public Color color;
        }
        class MyColorItemComparer : IComparer<MyColorItem>
        {
            public int Compare(MyColorItem x, MyColorItem y)
            {
                if (x.num < y.num) return 1;
                if (x.num > y.num) return -1;
                return (x.color.ToArgb() < y.color.ToArgb()) ? -1 :
                    ((x.color.ToArgb() > y.color.ToArgb()) ? 1 : 0);
            }
        }

        private void buttonPrint_Click(object sender, EventArgs e)
        {
            if (imageProcessor.TargetImage == null) return;

            List<Color> colorList2 = new List<Color>();
            // sort printed colors by frequency, unless there are way too many colors
            if (imageProcessor.TargetHistogram.Count <= 65536)
            {
                List<MyColorItem> colorList = new List<MyColorItem>();
                foreach (KeyValuePair<Color, int> p in imageProcessor.TargetHistogram)
                    colorList.Add(new MyColorItem(p.Value, p.Key));
                colorList.Sort(new MyColorItemComparer());
                foreach (MyColorItem i in colorList)
                    colorList2.Add(i.color);
            }
            else colorList2 = imageProcessor.TargetHistogram.GetColors();

            PrintForm f = new PrintForm(imageProcessor.TargetImage, 
                colorList2, imageProcessor.CustomColorPalette);
            f.Histogram = imageProcessor.TargetHistogram;
            if (f.ShowDialog() == DialogResult.OK)
            {
            }
        }

        private void tabPage1_Click(object sender, EventArgs e)
        {

        }

        private void buttonCombineColors_Click(object sender, EventArgs e)
        {
			if (selectedColors.Count == 0) return;
            List<Color> colors = new List<Color>();
            int r = 0, g = 0, b = 0;
            float sum = 0;
			foreach (int argb in selectedColors)
            {
				Color c = Color.FromArgb(argb);
                int n = 0;
                if (imageProcessor.TargetHistogram.TryGetValue(c, ref n))
                {
                    r += n * c.R;
                    g += n * c.G;
                    b += n * c.B;
                    sum += n;
                }
                colors.Add(c);
            }
            if (sum < 1) sum = 1;
            Color newColor = Color.FromArgb(
                Convert.ToInt32(r/sum),
                Convert.ToInt32(g/sum),
                Convert.ToInt32(b/sum));

			selectedColors.Clear();
			listView1.Invalidate();
            Cursor = Cursors.WaitCursor;
            if (imageProcessor.CustomColorPalette != null)
                newColor = imageProcessor.CustomColorPalette.FindMatch(newColor);
            recolorer.RemapColors(colors, newColor);
            Cursor = Cursors.Default;

        }

        private void buttonCrop_Click(object sender, EventArgs e)
        {
            if (croppedImage == null) return;
            int u = Convert.ToInt32(textBoxCropUp.Text);
            int d = Convert.ToInt32(textBoxCropDown.Text);
            int l = Convert.ToInt32(textBoxCropLeft.Text);
            int r = Convert.ToInt32(textBoxCropRight.Text);
            if (u > 100) u = 100;
            if (d > 100) d = 100;
            if (l > 100) l = 100;
            if (r > 100) r = 100;
            if (u + d >= 100) { u = 50; d = 49; }
            if (l + r >= 100) { l = 50; r = 49; }

            // continue from previous crop
            int w = croppedImage.Width;
            int h = croppedImage.Height;

            Rectangle rc = Rectangle.FromLTRB(l * w / 100, u * h / 100, (100 - r) * w / 100, (100 - d) * h / 100);

            Bitmap newImage = new Bitmap(w * (100 - l - r)/100, h * (100 - u - d)/100, loadedImage.PixelFormat);
            newImage.SetResolution(loadedImage.VerticalResolution, loadedImage.HorizontalResolution);
            using (Graphics g = Graphics.FromImage(newImage))
            {
                g.DrawImage(croppedImage, 0, 0, rc,GraphicsUnit.Pixel);
            }
            croppedImage = newImage;
            this.Cursor = Cursors.WaitCursor;
            textBoxCropDown.Text = "0";
            textBoxCropLeft.Text = "0";
            textBoxCropRight.Text = "0";
            textBoxCropUp.Text = "0";
//            drawingCanvas1.Picture = croppedImage;
            comboBoxPalette.SelectedIndex = 0;
            comboBoxColorPresets.SelectedIndex = 0;
            trackBarColors2.Value = trackBarColors2.Maximum;

            imageProcessor.OriginalImage = croppedImage;
            drawingCanvas1.CropCue.CropRect = Rectangle.FromLTRB(0, 0, 0, 0);
            textBoxHeight.Text = croppedImage.Height.ToString();
            textBoxWidth.Text = croppedImage.Width.ToString();
            this.Cursor = Cursors.Default;

        }

        private void buttonNoCrop_Click(object sender, EventArgs e)
        {
            textBoxCropDown.Text = "0";
            textBoxCropLeft.Text = "0";
            textBoxCropRight.Text = "0";
            textBoxCropUp.Text = "0";
            comboBoxPalette.SelectedIndex = 0;
            comboBoxColorPresets.SelectedIndex = 0;
            trackBarColors2.Value = trackBarColors2.Maximum;

            imageProcessor.OriginalImage = loadedImage;
            croppedImage = loadedImage;
        }

        private void textBoxCropUp_Leave(object sender, EventArgs e)
        {
//            UpdateCropCues();
        }

        private void textBoxCropUp_Validating(object sender, CancelEventArgs e)
        {
            TextBox tb = (TextBox)sender;
            int i = 0;
            try
            {
                i = Convert.ToInt32(tb.Text);
                if (i < 0 || i >= 100)
                {
                    e.Cancel = true;
                    errorProvider1.SetError(tb, Properties.Resources.ErrorCropNotValid1);
                }
                else
                {
                    errorProvider1.Clear();
                    UpdateCropCues();
                }
            }
            catch (Exception)
            {
                e.Cancel = true;
                errorProvider1.SetError(tb,Properties.Resources.ErrorCropNotValid1);
            }


        }

        private void buttonSortColors_Click(object sender, EventArgs e)
        {
            if (colorOrder < ColorOrdering.SortBySaturation)
                colorOrder++;
            else colorOrder = ColorOrdering.SortByCount;
            UpdateColorList(true);
        }

        private void comboBoxSortColors_SelectedIndexChanged(object sender, EventArgs e)
        {
            colorOrder = (ColorOrdering)comboBoxSortColors.SelectedIndex;
			// TODO: Fix this
            UpdateColorList(true);
        }

        private void listView1_MouseClick(object sender, MouseEventArgs e)
        {
            ListViewHitTestInfo h = listView1.HitTest(e.Location);
            if (h.Item != null) // && h.Location == ListViewHitTestLocations.Label)
            {
				h.Item.Checked = !h.Item.Checked;
				if (h.Item.Checked)
					selectedColors.Add(h.Item.BackColor.ToArgb());
				else selectedColors.Remove(h.Item.BackColor.ToArgb());
				listView1.RedrawItems(h.Item.Index, h.Item.Index, true);
				selectedColors.Sort();
				/*
                //If drawing with pen, then remove previous check mark
                if (h.Item.Selected == false &&
                    currentTool == DrawingToolType.Pen)
                {
                    foreach (ListViewItem l in listView1.Items)
                        if (l.Selected) l.Selected = false;
                }

                h.Item.Selected = !h.Item.Selected;
				 * */
            }
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Cursor = Cursors.WaitCursor;
            recolorer.Undo();
            Cursor = Cursors.Default;           
        }

        private void labelKuva_Click(object sender, EventArgs e)
        {
            if (loadedImage == null)
                buttonLoad_Click(sender, e);
        }

        private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
        {
            // TODO: show color value in status bar 
        }

        /// <summary>
        /// Handels mouse clicks on main picture.
        /// is thread tab is selected, check/uncheck color under mouse cursor.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void drawingCanvas1_MouseClick(object sender, MouseEventArgs e)
        {
            if (currentTool != DrawingToolType.None)
                tools[currentTool].Clicked(sender, e);
        }

        private void checkBoxDraw_CheckedChanged(object sender, EventArgs e)
        {
			currentTool = checkBoxDraw.Checked ? DrawingToolType.Pen :
				DrawingToolType.Pick;
			if (currentTool == DrawingToolType.Pen)
			{
				selectedColors.Clear();
				bool first = true;
				foreach (ListViewItem l in itemCache.Keys)
				{
					if (l.Checked)
					{
						if (first)
						{
							selectedColors.Add(l.BackColor.ToArgb());
							first = false ;
							l.EnsureVisible();
						}
						else l.Checked = false;
					}
				}
			}
			drawingCanvas1.PictureCursor = tools[currentTool].Cursor;
			listView1.Invalidate();

		}

        private void buttonAddColor_Click(object sender, EventArgs e)
        {
            Color newColor;
            // show custom color dialog or normal windows color dialog
            if (imageProcessor.CustomColorPalette != null)
            {
                ColorForm cf = new ColorForm();
                cf.ColorPalette = imageProcessor.CustomColorPalette;
                if (cf.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
                newColor = cf.SelectedColor;
            }
            else
            {
                if (colorDialog1.ShowDialog() != DialogResult.OK)
                {
                    return;
                }
                newColor = Color.FromArgb(colorDialog1.Color.R, colorDialog1.Color.G, colorDialog1.Color.B);
            }

            if (!addedColors.Contains(newColor) &&
                !imageProcessor.TargetHistogram.ContainsKey(newColor))
            {
                addedColors.Add(newColor);
                UpdateColorList(false);
            }
            
        }

        private void drawingCanvas1_MouseDown(object sender, MouseEventArgs e)
        {
            if (currentTool != DrawingToolType.None)
                tools[currentTool].Down(sender, e);

        }

        private void drawingCanvas1_MouseMove(object sender, MouseEventArgs e)
        {
            if (currentTool != DrawingToolType.None)
                tools[currentTool].Moved(sender, e);

        }

        private void drawingCanvas1_MouseUp(object sender, MouseEventArgs e)
        {
            if (currentTool != DrawingToolType.None)
                tools[currentTool].Up(sender, e);

        }

        private void toolStripDropDownButton1_Click(object sender, EventArgs e)
        {

        }

        private void fitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToolStripMenuItem item = sender as ToolStripMenuItem;
            if (item != null && item.Tag != null)
            {
                try
                {
                    int z = Convert.ToInt32(item.Tag);
                    drawingCanvas1.Zoom = z/ 100.0F;
                    foreach (ToolStripMenuItem m in toolStripDropDownButton2.DropDownItems)
                    {
                        m.Checked = false;
                    }
                    item.Checked = true ;

                }
                catch (Exception)
                {
                }
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            AboutBox1 a = new AboutBox1();
            a.ShowDialog();
        }

        private void contentsToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void userManualToolStripMenuItem_Click(object sender, EventArgs e)
        {
            HelpForm hf = new HelpForm();
            hf.Show();
        }

        private void tabPage3_Enter(object sender, EventArgs e)
        {
            if (currentTool == DrawingToolType.None)
                currentTool = DrawingToolType.Pick;
        }

        private void tabControl1_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (tabControl1.SelectedIndex < tabControl1.TabCount - 1)
            {
                if (!buttonNext.Enabled) buttonNext.Enabled = true;
            }
            else if (buttonNext.Enabled) buttonNext.Enabled = false;

        }

        private void Save()
        {
            if (imageProcessor.TargetImage == null) return;
            if (saveFileDialog1.FileName == null ||
                saveFileDialog1.FileName.Length == 0)
            {
                string file = openFileDialog1.FileName;
                // strip path
                int p = file.LastIndexOf('\\');
                if (p > 0 && file.Length > p)
                    file = file.Substring(p + 1);
                string tmp = file.ToLower();
                if (tmp.IndexOf(".rsd.") < 0 )
                {
                    int pos = file.LastIndexOf('.');
                    if (pos > 0)
                    {
                        file = file.Substring(0, pos) + ".rsd.png";
                    }
                }
                saveFileDialog1.FileName = file;
            }
            if (saveFileDialog1.ShowDialog() == DialogResult.OK)
            {
                string fn = saveFileDialog1.FileName.ToLower();
                try
                {
                    if (fn.EndsWith(".rsd.png"))
                    {
                        RyijyPng rpng = new RyijyPng();
                        rpng.Image = imageProcessor.TargetImage;
                        rpng.Palette = imageProcessor.CustomColorPalette;
                        rpng.Save(saveFileDialog1.FileName);
                    }
                    else if (fn.EndsWith(".jpg"))
                    {
                        imageProcessor.TargetImage.Save(saveFileDialog1.FileName, ImageFormat.Jpeg);
                    }
                    else if (fn.EndsWith(".bmp"))
                    {
                        imageProcessor.TargetImage.Save(saveFileDialog1.FileName, ImageFormat.Bmp);
                    }
                    else if (fn.EndsWith(".gif"))
                    {
                        imageProcessor.TargetImage.Save(saveFileDialog1.FileName, ImageFormat.Gif);
                    }
                    else imageProcessor.TargetImage.Save(saveFileDialog1.FileName);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }
        private void saveToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Save();
        }

        private void saveAsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Save();

        }
        private void OpenWebPage(string target)
        {
            try
            {
                System.Diagnostics.Process.Start(target);
            }
            catch
                (
                 System.ComponentModel.Win32Exception noBrowser)
            {
                if (noBrowser.ErrorCode == -2147467259)
                    MessageBox.Show(noBrowser.Message);
            }
            catch (System.Exception other)
            {
                MessageBox.Show(other.Message);
            }
        }
        private void wwwToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // open a webbrowser
            string target = "http://code.google.com/p/ryijy/";
            //Use no more than one assignment when you test this code. 
            //string target = "ftp://ftp.microsoft.com";
            //string target = "C:\\Program Files\\Microsoft Visual Studio\\INSTALL.HTM"; 
            OpenWebPage(target);
        }

        private void bugToolStripMenuItem_Click(object sender, EventArgs e)
        {
            OpenWebPage( "http://code.google.com/p/ryijy/issues/list" );
        }
        void SetLanguage(string lang)
        {
            if (MessageBox.Show(RyijyApp.Properties.Resources.NeedsRestart, 
                                RyijyApp.Properties.Resources.MainTitle, 
                                MessageBoxButtons.OKCancel) == DialogResult.OK)
            {
                RyijyApp.Properties.Settings.Default.Language = lang;
                RyijyApp.Properties.Settings.Default.Save();
                Application.Restart();
            }
        }

        private void englishToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLanguage("en");
        }

        private void finnishToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SetLanguage("fi");
        }
        void UpdateColorMatrix()
        {
            if (trackBarSaturation.Value != 100 ||
                trackBarContrast.Value != 100)
            {
                imageProcessor.ColorMatrix = MyColorMatrix.GetColorMatrix(
                    trackBarSaturation.Value/100.0f, trackBarContrast.Value/100.0f);
            }
            else
            {
                imageProcessor.ColorMatrix = null;
            }

        }
        private void trackBarContrast_Scroll(object sender, EventArgs e)
        {
            UpdateColorMatrix();
            labelContrastValue.Text = trackBarContrast.Value.ToString();

        }

        private void trackBar2_Scroll(object sender, EventArgs e)
        {
            UpdateColorMatrix();
            labelSaturationValue.Text = trackBarSaturation.Value.ToString();

        }

        private void colorTestToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ImageProcessor ip = new ImageProcessor();
            GalleryForm gf = new GalleryForm();
            gf.AddImage(loadedImage,"Loaded image");
            ip.OriginalImage = loadedImage;
            ip.ResizeImage(200, 150);
            ip.TargetDepth = 16;

            for (int i = 0; i < 11; i++)
            {
                ip.DiversityFactor = i / 10.0;
                ip.RemovedColors = 10;
                ip.UpdateTargetImage();
                string label = string.Format("{0}: {1} colors df={2}", i, ip.TargetColors, ip.DiversityFactor);
                gf.AddImage(ip.TargetImage, label);
/*
                ip.DiversityFactor = 0.5;
                ip.UpdateTargetImage();
                label = string.Format("{0}: {1} colors df={2}", i, ip.TargetColors, ip.DiversityFactor);
                gf.AddImage(ip.TargetImage, label);

                ip.DiversityFactor = 0.1;
                ip.UpdateTargetImage();
                label = string.Format("{0}: {1} colors df={2}", i, ip.TargetColors, ip.DiversityFactor);
                gf.AddImage(ip.TargetImage, label);
                */
            }

            gf.Show();
        }

        private void histogramToolStripMenuItem_Click(object sender, EventArgs e)
        {

        }

        private void distanceTestToolStripMenuItem_Click(object sender, EventArgs e)
        {
            string[] images = 
            { 
                "C:\\Users\\Public\\Pictures\\Sample Pictures\\Desert Landscape.jpg" ,
                "C:\\Users\\Public\\Pictures\\Sample Pictures\\Frangipani Flowers.jpg", 
                "C:\\Users\\Public\\Pictures\\Sample Pictures\\Green Sea Turtle.jpg", 
                "C:\\Users\\Public\\Pictures\\Sample Pictures\\Waterfall.jpg", 
            };
            ImageProcessor ip = new ImageProcessor();
            GalleryForm gf = new GalleryForm();
            foreach (string fn in images)
            {
                Bitmap img = (Bitmap) Bitmap.FromFile(fn);
                gf.AddImage(img, "fn");
                ip.OriginalImage = img;
                ip.ResizeImage(200, 150);
                ip.TargetDepth = 16;
                ip.RemovedColors = 16;

                foreach (ColorPicker.DistanceMode d in Enum.GetValues(typeof(ColorPicker.DistanceMode)))
                {
                    ip.ColorDistanceMode = d;
                    ip.UpdateTargetImage();
                    string label = d.ToString() + " " + ip.TargetColors;
                    gf.AddImage(ip.TargetImage, label);
                }
            }

            gf.Show();

        }


        private void textBoxColorTarget_TextChanged(object sender, EventArgs e)
        {
            string s = textBoxColorTarget.Text;
            string n="";
            int t = 0;
            for (int i=0;i<s.Length;i++)
            {
                if (s[i] >= '0' && s[i] <= '9')
                    n+= s[i];
                else if (t==0) t = i;
            }
            if (n != s)
            {
                textBoxColorTarget.Text = n;
                textBoxColorTarget.SelectionStart = t;

            }
        }

        private void textBoxColorTarget_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
        {
            if (e.KeyCode == Keys.Return)
            {
                try 
                {
                    int n = Convert.ToInt32(textBoxColorTarget.Text);
                    if (n == 0)
                    {                        
                        n = 1;
                        textBoxColorTarget.Text = n.ToString();
                    }
                    trackBarColors2.Value = n;
                }
                catch (Exception)
                {
                }
                return;
            }
            if (e.KeyCode == Keys.Escape)
            {
                textBoxColorTarget.Text = imageProcessor.RemovedColors.ToString();
                return;
            }
        }

        private void textBoxColorTarget_Leave(object sender, EventArgs e)
        {
            try
            {
                int n = Convert.ToInt32(textBoxColorTarget.Text);
                if (n == 0)
                {
                    n = 1;
                    textBoxColorTarget.Text = n.ToString();
                }
                if (trackBarColors2.Value != n)
                    trackBarColors2.Value = n;
            }
            catch (Exception)
            {
                textBoxColorTarget.Text = trackBarColors2.Value.ToString();
            }

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // hide test menu in release builds
#if !DEBUG 
            testToolStripMenuItem.Visible = false;
#endif
            labelContrastValue.Text = trackBarContrast.Value.ToString();
            labelSaturationValue.Text = trackBarSaturation.Value.ToString();
            textBoxColorTarget.Text = trackBarColors2.Value.ToString();
            LoadPalettes();
			// Need to set splitter if font is larger than normal.
			if (buttonLoad.Left < 0)
			{
				Point pt = new Point(buttonLoad.Left, buttonLoad.Top);
				splitContainer1.SplitterDistance = Convert.ToInt32(splitContainer1.PointToClient(splitContainer2.PointToScreen(pt)).X * 0.99);
			}
           
        }
        private void LoadPalettes()
        {
            string path = "";
            List<string> files = new List<string>();
            // get also from previous versions
            if (RyijyApp.Properties.Settings.Default.Standalone)
            {
                path = Application.StartupPath;
                files.AddRange(System.IO.Directory.GetFiles(Application.StartupPath, "*_palette.txt"));
            }
            else
            {
                // Load older files
                path = Application.UserAppDataPath;
                int pos = path.LastIndexOf('\\');
                path = path.Substring(0, pos);
                string path132 = path + "\\1.3.2.0";
                if (System.IO.Directory.Exists(path132))
                    files.AddRange(System.IO.Directory.GetFiles(path132, "*_palette.txt"));
                string path133 = path + "\\1.3.3.0";
                if (System.IO.Directory.Exists(path133))
                    files.AddRange(System.IO.Directory.GetFiles(path133, "*_palette.txt"));

                // New files are not under version number
                if (!System.IO.Directory.Exists(path))
                    System.IO.Directory.CreateDirectory(path);
                files.AddRange(System.IO.Directory.GetFiles(path, "*_palette.txt"));

            }

            foreach (string f in files)
            {
                try
                {
                    CustomPalette cp = new CustomPalette();
                    if (cp.Load(f))
                    {
                        cp.UserPalette = true;
                        customPalettes.Add(cp);
                        comboBoxPalette.Items.Add(cp.PaletteName);
                    }
                }
                catch (Exception)
                {
                }
            }
        }
        private void trackBarColors2_ValueChanged(object sender, EventArgs e)
        {
            textBoxColorTarget.Text = trackBarColors2.Value.ToString();
            if (trackBarColors2.Value != imageProcessor.TargetColors)
            {
                trackBarColors2_Scroll(this, EventArgs.Empty);
            }
        }

        private void dumpColorTreeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ColorLib.CubePicker cp = new ColorLib.CubePicker(imageProcessor.PaletteHistogram, ColorLib.CubePicker.CubeMode.Percent);
            ColorLib.CubePicker.DumpVisitor dv = new ColorLib.CubePicker.DumpVisitor();
            cp.Cube.Apply(dv);
            using (System.IO.StreamWriter sw = new System.IO.StreamWriter("cubedump2.txt"))
            {
                sw.Write(dv.Dump);
            }
        }

        private void paletteManaerToolStripMenuItem_Click(object sender, EventArgs e)
        {
            PaletteMgrForm pl = new PaletteMgrForm();
            List<CustomPalette> list = new List<CustomPalette>();
            list.Add(LoadDMC());
            pl.UserPaletteList = customPalettes;
            pl.SystemPaletteList = list;

            pl.ShowDialog();

            // update palette combobox
            int next = 5;
            if (comboBoxPalette.SelectedIndex>=next)
                comboBoxPalette.SelectedIndex = 0;
            foreach (CustomPalette cp in customPalettes)
            {
                if (comboBoxPalette.Items.Count > next)
                    comboBoxPalette.Items[next] = cp.PaletteName;
                else comboBoxPalette.Items.Add(cp.PaletteName);
                next++;
            }
            while (comboBoxPalette.Items.Count > next)
                comboBoxPalette.Items.RemoveAt(next);
        }

		private void listView1_DrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e)
		{
			e.DrawDefault = true;
		}

		private void listView1_DrawItem(object sender, DrawListViewItemEventArgs e)
		{
			e.DrawBackground();
			Rectangle r = new Rectangle(e.Bounds.X, e.Bounds.Y, e.Bounds.Width-1, e.Bounds.Height-1);
			e.Graphics.FillRectangle(Brushes.White,
				new Rectangle(e.Bounds.X, e.Bounds.Y , 8, e.Bounds.Height));
			if (e.Item.Checked)
			{
				e.Graphics.FillRectangle(Brushes.CornflowerBlue,
				new Rectangle(e.Bounds.X, e.Bounds.Y + e.Bounds.Height / 4, 5, e.Bounds.Height / 2));
			}
			else
			{
				
				 
			}
			if ((e.State & ListViewItemStates.Focused) != 0)
			{
				if (e.Bounds.Width>8)
					e.Graphics.DrawRectangle(dotpen, e.Bounds.X+8, e.Bounds.Y, e.Bounds.Width-9, e.Bounds.Height-1);
			}
			e.Graphics.DrawRectangle(Pens.DarkGray,
				new Rectangle(e.Bounds.X, e.Bounds.Y + e.Bounds.Height / 4, 5, e.Bounds.Height / 2));

			if (listView1.View != View.Details)
				e.DrawText();
		}

		private void listView1_DrawSubItem(object sender, DrawListViewSubItemEventArgs e)
		{
			StringFormat sf = new StringFormat();
			sf.Trimming = StringTrimming.EllipsisCharacter;
			sf.FormatFlags = StringFormatFlags.NoWrap;
			sf.LineAlignment = StringAlignment.Center;
			Brush br = (e.Item.BackColor.GetBrightness() < 0.4) ? Brushes.White : Brushes.Black;
			Rectangle r = e.Bounds;
			if (e.ColumnIndex == 0)
			{
				r.Width = r.Width - 8;
				r.Offset(8, 0);
			}
			e.Graphics.DrawString(e.SubItem.Text, this.Font, br, r,sf);
		}

		private void listView1_VirtualItemsSelectionRangeChanged(object sender, ListViewVirtualItemsSelectionRangeChangedEventArgs e)
		{
			if (itemCache == null) return;

			/* Not working as supposed to. Selecting range upwards results in top item being deselected.
			// only use for adding shift-clicked range
			if (!e.IsSelected)
			{
				return;
			}
			int n = listView1.FocusedItem.Index;
			for (int i = e.StartIndex; i <= e.EndIndex; i++)
			{
				int color = itemCache.Keys[i].BackColor.ToArgb();
				if (e.IsSelected)
				{
					if (!selectedColors.Contains(color))
					{
						selectedColors.Add(color);
						itemCache.Keys[i].Checked = true;
						listView1.RedrawItems(i, i, true);
					}
				}
			}
			selectedColors.Sort();
			*/
		}

		// Forces each row to repaint itself the first time the mouse moves over 
		// it, compensating for an extra DrawItem event sent by the wrapped 
		// Win32 control.
		private void listView1_MouseMove(object sender, MouseEventArgs e)
		{
			ListViewItem item = listView1.GetItemAt(5, e.Y);
			if ((item != null) && (item.Tag == null))
			{
				listView1.Invalidate(item.Bounds);
				item.Tag = "tag";
			}

		}

		private void listView1_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
		{
			/* Not used when checked state is updated by mouseclick handler
			if (itemCache == null) return;

			int color = e.Item.BackColor.ToArgb();
			if (e.IsSelected)
			{
				if (!selectedColors.Contains(color))
					selectedColors.Add(color);
			}
			else
			{
				if (selectedColors.Contains(color))
					selectedColors.Remove(color);
			}
			*/
		}

		/// <summary>
		/// This event comes when user dismisses error box complaining
		/// too many colors over color reduction slider.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void labelColorsError_Click(object sender, EventArgs e)
		{
			if (comboBoxPalette.SelectedIndex == 1)
				comboBoxPalette.SelectedIndex = 2;
			else comboBoxPalette.SelectedIndex = 1;
		}

		/// <summary>
		/// This button indicates that user want to udpate color list 
		/// on threads page even if it takes long time.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void buttonUpdateColorlist_Click(object sender, EventArgs e)
		{
			Cursor = Cursors.WaitCursor;
			UpdateColorList(true, true);
			Cursor = Cursors.Default;

		}

        private void copyToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (drawingCanvas1.Picture != null)
                Clipboard.SetImage(drawingCanvas1.Picture);
        }

        private void pasteToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (!Clipboard.ContainsImage())
                return;
            loadedImage = new Bitmap(Clipboard.GetImage());
            croppedImage = loadedImage;
            imageProcessor.OriginalImage = null;
            drawingCanvas1.Picture = loadedImage;
            int oldPalette = comboBoxPalette.SelectedIndex;
            comboBoxColorPresets.SelectedIndex = 0;
            comboBoxSizes.SelectedIndex = 0;
            textBoxHeight.Text = loadedImage.Height.ToString();
            textBoxWidth.Text = loadedImage.Width.ToString();
            textBoxCropDown.Text = "0";
            textBoxCropLeft.Text = "0";
            textBoxCropRight.Text = "0";
            textBoxCropUp.Text = "0";
            buttonNext.Enabled = true;
            label13.Visible = false;
            drawingCanvas1.FitToWindow = true;
            imageProcessor.OriginalImage = loadedImage;
            comboBoxPalette.SelectedIndex = oldPalette;
            UpdateInfoBox();
            saveFileDialog1.FileName = "";
        }

    }
}